package simpledb.execution;

import simpledb.common.Database;
import simpledb.common.DbException;
import simpledb.common.Type;
import simpledb.storage.IntField;
import simpledb.storage.Tuple;
import simpledb.storage.TupleDesc;
import simpledb.transaction.TransactionAbortedException;
import simpledb.transaction.TransactionId;

/**
 * Delete 算子：从其子算子中读取元组，并将这些元组从它们所属的表中删除。
 * 每次调用 next() 方法时执行一次删除操作，返回一个包含删除记录数的单字段元组。
 * 该算子是数据库中实现 DELETE 语句的核心组件，支持通过事务管理数据一致性。
 */
public class Delete extends Operator {

    private static final long serialVersionUID = 1L;

    // 当前事务ID
    private TransactionId     tid;

    // 子算子，用于获取要删除的元组
    private OpIterator        child;

    // 要删除数据的目标表的 ID
    private int               tableId;

    // 返回值的 TupleDesc，只有一个 INT 类型字段，表示删除数量
    private TupleDesc         td;

    // 是否已经执行过 fetchNext
    private boolean           isFetched;

    /**
     * 构造函数。
     *
     * @param t         执行删除操作的事务
     * @param child     子算子，提供要删除的元组
     */
    public Delete(TransactionId t, OpIterator child) {
        this.tid = t;
        this.child = child;

        // 插入结果返回一个 INT 类型字段，表示删除的数量
        final Type[] types = new Type[] { Type.INT_TYPE };
        this.td = new TupleDesc(types);
        this.isFetched = false;
    }

    /**
     * 获取该算子输出的元组描述信息。
     * 这里始终返回一个 INT 字段，表示删除记录的数量。
     */
    public TupleDesc getTupleDesc() {
        return this.td;
    }

    /**
     * 打开该算子及其子算子。
     * @throws DbException
     * @throws TransactionAbortedException
     */
    public void open() throws DbException, TransactionAbortedException {
        this.child.open();
        super.open();
        isFetched = false; // 重置是否已执行标志
    }

    /**
     * 关闭该算子及其子算子。
     */
    public void close() {
        this.child.close();
        super.close();
    }

    /**
     * 重置该算子，使其可以重新读取数据。
     * 实现方式是先关闭再打开。
     */
    public void rewind() throws DbException, TransactionAbortedException {
        close();
        open();
    }

    /**
     * 从子算子中读取所有元组，并通过 BufferPool 将它们从目标表中删除。
     * 返回一个包含删除记录数量的单字段元组。
     *
     * 注意：
     * - 删除操作通过 Database.getBufferPool().deleteTuple() 完成；
     * - 不需要检查是否存在该元组；
     * - 只能调用一次，多次调用返回 null。
     *
     * @return 包含删除记录数量的单字段元组，或 null（如果被调用超过一次）
     * @throws TransactionAbortedException
     * @throws DbException
     */
    protected Tuple fetchNext() throws TransactionAbortedException, DbException {
        int cnt = 0;

        // 从子算子中逐个读取元组并删除
        while (this.child.hasNext()) {
            final Tuple next = this.child.next();
            try {
                Database.getBufferPool().deleteTuple(this.tid, next);
            } catch (Exception e) {
                System.out.println("删除元组时发生错误: " + e.getMessage());
                e.printStackTrace();
            }
            cnt++;
        }

        // 如果已经执行过一次且没有新数据，则返回 null
        if (cnt == 0 && isFetched) {
            return null;
        }

        isFetched = true;

        // 构造返回结果：一个包含删除数量的元组
        final Tuple result = new Tuple(this.td);
        result.setField(0, new IntField(cnt));

        return result;
    }

    /**
     * 获取当前算子的所有子节点（即子算子）。
     * 对于 Delete 算子来说，只有一个子算子。
     */
    @Override
    public OpIterator[] getChildren() {
        return new OpIterator[] { this.child };
    }

    /**
     * 设置当前算子的子节点。
     * 如果传入了新的子算子数组，则更新当前 child。
     */
    @Override
    public void setChildren(OpIterator[] children) {
        if (children.length > 0) {
            this.child = children[0];
        }
    }
}
